Index: web/standard/src/main/webapp/resources/js/xwiki/usersandgroups/usersandgroups.js
===================================================================
--- web/standard/src/main/webapp/resources/js/xwiki/usersandgroups/usersandgroups.js	(revision 29535)
+++ web/standard/src/main/webapp/resources/js/xwiki/usersandgroups/usersandgroups.js	(working copy)
@@ -196,7 +196,7 @@
 /**
   * user list element creator. Used in adminusers.vm.
   */
-function displayUsers(row, i, table)
+function displayUsers(row, i, table, form_token)
 {
   var userurl = row.userurl;
   var usersaveurl = row.usersaveurl;
@@ -246,7 +246,7 @@
       del.className = 'icon-manageg';
     } else {
       del.src = '$xwiki.getSkinFile("js/xwiki/usersandgroups/img/clear.png")';
-      Event.observe(del, 'click', deleteUserOrGroup(i, table, row.fullname, "user"));
+      Event.observe(del, 'click', deleteUserOrGroup(i, table, row.fullname, "user", form_token));
       del.className = 'icon-manage';
     }
     del.title = "$msg.get('delete')";
@@ -258,7 +258,7 @@
 }
 
 /** group list element creator **/
-function displayGroups(row, i, table)
+function displayGroups(row, i, table, form_token)
 {
   var userurl = row.userurl;
   var userinlineurl = row.userinlineurl;
@@ -296,7 +296,7 @@
     var del = document.createElement('img');
     del.src = '$xwiki.getSkinFile("js/xwiki/usersandgroups/img/clear.png")';
     del.title = "$msg.get('delete')";
-    Event.observe(del, 'click', deleteUserOrGroup(i, table, row.fullname, "group"));
+    Event.observe(del, 'click', deleteUserOrGroup(i, table, row.fullname, "group", form_token));
     del.className = 'icon-manage';
 
     //edit user
@@ -316,7 +316,7 @@
 }
 
 /** group members list element creator **/
-function displayMembers(row, i, table)
+function displayMembers(row, i, table, form_token)
 {
   var tr = document.createElement('tr');
   var membername = document.createElement("td");
@@ -345,7 +345,7 @@
       del.className = 'icon-manageg';
     } else {
       del.src = '$xwiki.getSkinFile("js/xwiki/usersandgroups/img/clear.png")';
-      Event.observe(del, 'click', deleteMember(i, table, row.fullname, row.docurl));
+      Event.observe(del, 'click', deleteMember(i, table, row.fullname, row.docurl, form_token));
       del.className = 'icon-manage';
     }
     del.title = "$msg.get('delete')";
@@ -361,7 +361,7 @@
   * Used in adminglobalrights.vm, adminspacerights.vm, editrights.vm.
   * @todo allows and denys should be arrays, not strings.
   */
-function displayUsersAndGroups(row, i, table, idx)
+function displayUsersAndGroups(row, i, table, idx, form_token)
 {
   var userurl = row.userurl;
   var uorg = table.json.uorg;
@@ -369,6 +369,9 @@
   var denys = row.denys;
 
   var saveUrl = window.docviewurl + "?xpage=saverights&clsname=" + table.json.clsname + "&fullname=" + row.fullname + "&uorg=" + uorg;
+  if (form_token != undefined) {
+      saveUrl += "&form_token=" + form_token;
+  }
 
   var tr = document.createElement('tr');
 
@@ -414,10 +417,13 @@
 
 
 //function to delete a user with ajax
-function deleteUserOrGroup(i, table, docname, uorg)
+function deleteUserOrGroup(i, table, docname, uorg, form_token)
 {
   return function() {
     var url = "?xpage=deleteuorg&docname=" + docname;
+    if (form_token != undefined) {
+        url += "&form_token=" + form_token;
+    }
     if (uorg == "user") {
       if (confirm("$msg.get('rightsmanager.confirmdeleteuser').replaceAll('"', '\\"')".replace('__name__', docname))) {
         new Ajax.Request(url, {
@@ -441,10 +447,13 @@
 }
 
 //deletes a member of a group (only the object)
-function deleteMember(i, table, docname, docurl)
+function deleteMember(i, table, docname, docurl, form_token)
 {
   return function() {
     var url = docurl + "?xpage=deletegroupmember&fullname=" + docname;
+    if (form_token != undefined) {
+      url += "&form_token=" + form_token;
+    }
     if (confirm("$msg.get('rightsmanager.confirmdeletemember').replaceAll('"', '\\"')")) {
       new Ajax.Request(url, {
         method: 'get',
Index: web/standard/src/main/webapp/templates/copy.vm
===================================================================
--- web/standard/src/main/webapp/templates/copy.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/copy.vm	(working copy)
@@ -24,7 +24,7 @@
     #end
     #set($sourcewiki = $context.database)
     #set($targetwiki = $context.database)
-    #if($targetdoc != "")
+    #if($targetdoc != "" && $!{services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
     #set($ok = $xwiki.copyDocument($sourcedoc, $targetdoc, $sourcewiki, $targetwiki, $language, false, true))
     #set($prefixedSourcedoc = "#if($sourcewiki!=$context.database)$sourcewiki:#end$sourcedoc")
     #set($prefixedTargetdoc = "#if($targetwiki!=$context.database)$targetwiki:#end$targetdoc")
@@ -40,6 +40,8 @@
     #info("$msg.get('core.copy.copyingdoc', [$sourcelink, $targetlink])")
     #else
     <form action="">
+    ## CSRF prevention
+    <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
     <div class="hidden"><input type="hidden" name="xpage" value="copy" /></div>
     <table>
     <tr><td>$msg.get("core.copy.sourcedoc"):</td>
Index: web/standard/src/main/webapp/templates/commentsinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/commentsinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/commentsinline.vm	(working copy)
@@ -152,7 +152,7 @@
         #end
       #end
       #if($hasEdit) 
-        <span class="commenttool commentdelete"><a class="delete" href="$doc.getURL('objectremove', "classname=${xCommentClass}&amp;classid=${comment.number}&amp;xredirect=$xredirect")" title="$msg.get('core.viewers.comments.delete')">$msg.get('core.viewers.comments.delete')</a></span>
+        <span class="commenttool commentdelete"><a class="delete" href="$doc.getURL('objectremove', "form_token=$!{services.csrf.getToken()}&amp;classname=${xCommentClass}&amp;classid=${comment.number}&amp;xredirect=$xredirect")" title="$msg.get('core.viewers.comments.delete')">$msg.get('core.viewers.comments.delete')</a></span>
       #end
       </span>## commenttools
     </div>## commentheader
@@ -165,6 +165,8 @@
 #macro(displayCommentForm)
   #if($xwiki.hasAccessLevel('comment'))
     <form action="$doc.getURL('commentadd', "xpage=view&amp;viewer=comments#commentform")" method="post" id="AddComment">
+      ## CSRF prevention
+      <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
       <fieldset class="expanded" id="commentform">
         <legend>$msg.get('core.viewers.comments.add.title')</legend>
         #if($context.user != 'XWiki.XWikiGuest')
@@ -231,6 +233,8 @@
 #macro(displayEditCommentForm $comment)
   #if (${comment.author} == ${context.user})
   <form action="$doc.getURL('save')" method="post" class="edit-xcomment">
+    ## CSRF prevention
+    <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
     <div id="xwikicomment_${comment.number}" class="xwikicomment#if($comment.getProperty('author').value == $doc.creator) commentByCreator#end">
     <div class="commentheader">
       <div>
Index: web/standard/src/main/webapp/templates/changemyavatar.vm
===================================================================
--- web/standard/src/main/webapp/templates/changemyavatar.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/changemyavatar.vm	(working copy)
@@ -11,7 +11,9 @@
   #if($context.user == $doc.fullName || $hasAdmin)
     <form action="$doc.getURL("upload")" enctype="multipart/form-data" method="post" onsubmit="if(this.filepath.value == ''){ return false; }"><div>
       <div id="document-title"><h1>$msg.get("changephoto", [$xwiki.getUserName($doc.fullName, false)])</h1></div>
-      <input type="hidden" name="xredirect" value="$doc.getURL("view", "xpage=changeavatar")"/>
+      ## CSRF prevention
+      <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
+      <input type="hidden" name="xredirect" value="$doc.getURL("view", "xpage=changeavatar&form_token=$!{services.csrf.getToken()}")"/>
       <div><label id="xwikiuploadfilelabel" for="xwikiuploadfile">$msg.get('uploadavatarfile'):</label></div>
       <div><input id="xwikiuploadfile" type="file" name="filepath" value="" size="40"/></div>
       <div class="padded">
Index: web/standard/src/main/webapp/templates/deleteuorg.vm
===================================================================
--- web/standard/src/main/webapp/templates/deleteuorg.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/deleteuorg.vm	(working copy)
@@ -1,5 +1,10 @@
+## CSRF check
+#if(${services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
 #set($docname = $request.get("docname"))
 #set($d = $xwiki.getDocument($docname))
 $d.delete()
 ## $d.delete doesn't return anything, so we need to print some text in order to avoid a "This template does not exist" response.
 OK
+#else
+FAIL
+#end
Index: web/standard/src/main/webapp/templates/delete.vm
===================================================================
--- web/standard/src/main/webapp/templates/delete.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/delete.vm	(working copy)
@@ -52,11 +52,13 @@
     #set($fullmsg = $msg.get('core.delete.confirm'))
   #end
 #end
-#xwikimessagebox($msg.get('core.delete') $fullmsg $doc.getURL('delete', "confirm=1$!{redirectparam}&amp;$languageparams") $doc.getURL('view') $msg.get('yes') $msg.get('no'))
+#xwikimessagebox($msg.get('core.delete') $fullmsg $doc.getURL('delete', "confirm=1$!{redirectparam}&amp;form_token=$!{services.csrf.getToken()}&amp;$languageparams") $doc.getURL('view') $msg.get('yes') $msg.get('no'))
 #else ## request.id
   #xwikimessageboxstart($msg.get('core.delete') $msg.get('core.recyclebin.completelyDeleteConfirm'))
     <form action="" method="post">
       <div class="hidden">
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
         <input type="hidden" name="confirm" value="1"/>
         <input type="hidden" name="id" value="$request.id"/>
         #if("$!{request.xredirect}" != '')
Index: web/standard/src/main/webapp/templates/edit.vm
===================================================================
--- web/standard/src/main/webapp/templates/edit.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/edit.vm	(working copy)
@@ -33,6 +33,8 @@
 #template("menuedit.vm")
 #if($editor == 'wiki' || $editor == 'wysiwyg')
   <form id="edit" method="post" action="$doc.getURL('preview')" >
+  ## CSRF prevention
+  <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
   ## Pass the section number when the form is submitted in order to affect only the specified section.
   ## We need to test if the section number is present to prevent a NumberFormatException for the empty string.
   #if("$!{request.section}" != '')
Index: web/standard/src/main/webapp/templates/rollback.vm
===================================================================
--- web/standard/src/main/webapp/templates/rollback.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/rollback.vm	(working copy)
@@ -10,6 +10,8 @@
     #xwikimessageboxstart($msg.get('rollback') $msg.get('readytorollback').concat(' ').concat($request.get('rev')).concat('?'))
     <form action="" method="post">
       <div>
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
         <input type="hidden" name="confirm" value="1" />
         <span class="buttonwrapper"><input type="submit" class="button" value="$msg.get('yes')"/></span>
         <span class="buttonwrapper"><a class="secondary button" href="$doc.getURL('view')">$msg.get('no')</a></span>
Index: web/standard/src/main/webapp/templates/resetversions.vm
===================================================================
--- web/standard/src/main/webapp/templates/resetversions.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/resetversions.vm	(working copy)
@@ -5,7 +5,7 @@
 #template("startpage.vm")
 <div class="main layoutsubsection">
 <div id="mainContentArea">
-#xwikimessagebox($msg.get("resetversions") $msg.get("confirmresetversions") $doc.getURL("reset", "confirm=1") $doc.getURL("view") $msg.get("yes") $msg.get("no"))
+#xwikimessagebox($msg.get("resetversions") $msg.get("confirmresetversions") $doc.getURL("reset", "confirm=1&amp;form_token=$!{services.csrf.getToken()}") $doc.getURL("view") $msg.get("yes") $msg.get("no"))
 <div class="clearfloats"></div>
 </div>## mainContentArea
 </div>## main
Index: web/standard/src/main/webapp/templates/changeavatar.vm
===================================================================
--- web/standard/src/main/webapp/templates/changeavatar.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/changeavatar.vm	(working copy)
@@ -15,7 +15,7 @@
 #set($name = $attachment.filename)
 #end
 #end
-$response.sendRedirect($doc.getURL("save", "XWiki.XWikiUsers_0_avatar=$name"))
+$response.sendRedirect($doc.getURL("save", "XWiki.XWikiUsers_0_avatar=$name&form_token=$!{request.getParameter('form_token')}"))
 #else
 $response.sendRedirect($doc.getURL("view"))
 #end
\ No newline at end of file
Index: web/standard/src/main/webapp/templates/editwysiwyg.vm
===================================================================
--- web/standard/src/main/webapp/templates/editwysiwyg.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editwysiwyg.vm	(working copy)
@@ -10,6 +10,8 @@
 ## <div class="topbuttons">#template("editactions.vm")</div>
 <div>
 <div class="hidden">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <input type="hidden" name="template" value="$!request.template" />
 #if($request.section && $request.section!="")
  #set($xcontinue=$doc.getURL("edit", "editor=wysiwyg&amp;section=$request.section"))
Index: web/standard/src/main/webapp/templates/userinactive.vm
===================================================================
--- web/standard/src/main/webapp/templates/userinactive.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/userinactive.vm	(working copy)
@@ -17,6 +17,8 @@
 $msg.get("accountnotactive_email")
 <form action="${confirmdoc.getURL()}" method="post">
 <div>
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <input type="hidden" name="xwikiname" value="$context.user" />
 <input type="text" name="validkey" size="16" />
 <span class="buttonwrapper"><input type="submit" value="$msg.get('confirmaccount')" class="button"/></span>
Index: web/standard/src/main/webapp/templates/historyinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/historyinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/historyinline.vm	(working copy)
@@ -46,6 +46,8 @@
 ##
     #set($formname="historyform")
     <form id="$formname" action="$doc.getURL('view', "viewer=changes&amp;$docvariant")" method="post">
+      ## CSRF prevention
+      <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
       <div id="_history">
       <div class="centered">
       <table class="xwikidatatable" summary="$msg.get('core.viewers.history.summary', [$xwiki.getXMLEncoded($doc.displayTitle), $versions.get($util.add($versions.size(), -1)), $versions.get(0)])">
@@ -94,11 +96,11 @@
         #if ($tdoc.getRCSVersion()==$version)
           <td><span class="currentversionlabel">$msg.get('core.viewers.history.currentVersion')</span></td>
         #else
-          <td class="xwikibuttonlink"><a href="$tdoc.getURL('rollback', "rev=$version$lang")" onclick="if (confirm('$msg.get('core.viewers.history.confirmRollback', [$version])')){this.href += '&amp;confirm=1'; return true;} return false;">$msg.get('core.viewers.history.rollback')</a></td>
+          <td class="xwikibuttonlink"><a href="$tdoc.getURL('rollback', "form_token=$!{services.csrf.getToken()}&amp;rev=$version$lang")" onclick="if (confirm('$msg.get('core.viewers.history.confirmRollback', [$version])')){this.href += '&amp;confirm=1'; return true;} return false;">$msg.get('core.viewers.history.rollback')</a></td>
         #end
     #end
     #if($hasAdmin)
-          <td class="xwikibuttonlink"><a href="$tdoc.getURL('deleteversions',"rev=$version$lang")" onclick="if (confirm('$msg.get('core.viewers.history.confirmDeleteSingle', [$version])')){this.href += '&amp;confirm=1'; return true;} return false;">$msg.get('core.viewers.history.deleteSingle')</a></td>
+          <td class="xwikibuttonlink"><a href="$tdoc.getURL('deleteversions',"form_token=$!{services.csrf.getToken()}&amp;rev=$version$lang")" onclick="if (confirm('$msg.get('core.viewers.history.confirmDeleteSingle', [$version])')){this.href += '&amp;confirm=1'; return true;} return false;">$msg.get('core.viewers.history.deleteSingle')</a></td>
     #end
         </tr>
   #end ## foreach
Index: web/standard/src/main/webapp/templates/editobject.vm
===================================================================
--- web/standard/src/main/webapp/templates/editobject.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editobject.vm	(working copy)
@@ -11,7 +11,7 @@
 #macro(displayObject $obj $class $props $firstfield)
   <div id="xobject_${class.name}_${obj.number}" class="xobject">
   <div id="xobject_${class.name}_${obj.number}_title" class="xobject-title"><h3>#cleanClassname(${class.name}) ${obj.number}: $doc.display($firstfield, $obj)
-    <a href="$doc.getURL("objectremove", "classname=${class.name}&amp;classid=${obj.number}&amp;xredirect=${doc.getURL('edit', 'editor=object')}")" class="xobject-action delete" title="Remove this object">$msg.get("removethisobject")</a>
+    <a href="$doc.getURL("objectremove", "form_token=$!{services.csrf.getToken()}&amp;classname=${class.name}&amp;classid=${obj.number}&amp;xredirect=${doc.getURL('edit', 'editor=object')}")" class="xobject-action delete" title="Remove this object">$msg.get("removethisobject")</a>
     #if(!$hasCustomObject) <a href="$doc.getURL("edit", "editor=object&amp;classname=${class.name}&amp;object=${obj.number}")" class="xobject-action edit" title="Edit only this object">$msg.get("editthisobject")</a> #end
   </h3></div>
   <div id="xobject_${class.name}_${obj.number}_content" class="xobject-content"><div>
@@ -92,6 +92,8 @@
   #end
   #set($redirect = $xwiki.getRequestURL())
   <form id="update" method="post" action="$doc.getURL("save")" onsubmit="cancelCancelEdit()">
+  ## CSRF prevention
+  <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
   <div id="xwikieditcontent">
   ## This input is used by the full screen widget: it stores the selector of the maximized element, so that the full screen can be restored when coming back from preview.
   <input type="hidden" name="x-maximized" value="$!{request.get('x-maximized')}" />
Index: web/standard/src/main/webapp/templates/renameStep1.vm
===================================================================
--- web/standard/src/main/webapp/templates/renameStep1.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/renameStep1.vm	(working copy)
@@ -6,6 +6,8 @@
 #set($newname = $msg.get("core.rename.inputPrompt"))
 <form action="?xpage=rename&amp;step=2" method="post"
     onsubmit="if(this.newPageName.value=='${escapetool.xml($newname)}') this.newPageName.value=''">
+  ## CSRF prevention
+  <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
   <h2><span>$msg.get("core.rename.title.newName")</span></h2>
   #set($pweb = $doc.space)
   <div class="padded">
Index: web/standard/src/main/webapp/templates/adduorg.vm
===================================================================
--- web/standard/src/main/webapp/templates/adduorg.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/adduorg.vm	(working copy)
@@ -5,7 +5,7 @@
 #if($uorg == "user" || $uorg == "group")
   ## verify if the user or group is already in the group
   #set($exists = "$!doc.getObject('XWiki.XWikiGroups', 'member', $fullname)")
-  #if($exists == "")
+  #if($exists == "" && $!{services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
     1
     #set($obj = $doc.newObject("XWiki.XWikiGroups"))
     $obj.set("member", $fullname)
Index: web/standard/src/main/webapp/templates/documentTags.vm
===================================================================
--- web/standard/src/main/webapp/templates/documentTags.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/documentTags.vm	(working copy)
@@ -68,6 +68,8 @@
 ##
 #macro(displayAddForm)
   <form action="$doc.getURL('view', "xpage=documentTags&amp;xaction=add&amp;xredirect=${xredirect}")" method="post" class="tag-add-form">
+    ## CSRF prevention
+    <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
     <div>
       <label>$msg.get('core.tags.add.label')<br/>
       <input class="input-tag" type="text" name="tag" autocomplete="off"/></label><br/>
Index: web/standard/src/main/webapp/templates/editgroup.vm
===================================================================
--- web/standard/src/main/webapp/templates/editgroup.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editgroup.vm	(working copy)
@@ -71,7 +71,8 @@
 //<![CDATA[
 (function() {
  var startup = function() {
-  tabl = new ASSTable("$url", 15, "display1", "scrollbar1", "memberstable", displayMembers, true);
+  var callback = function(row, i, table) { return displayMembers(row, i, table, form_token) };
+  tabl = new ASSTable("$url", 15, "display1", "scrollbar1", "memberstable", callback, true);
    
   //create new ajax suggest
    suggesturl = "$doc.getURL()" + "?xpage=uorgsuggest" + "&" + "classname=XWiki.XWikiUsers" + "&" + "wiki=local";
@@ -96,6 +97,7 @@
      if(name) 
      {
              var url = "$doc.getURL("view")" + "?xpage=adduorg" + "&" + "uorg=" + uorg + "&" + "name=" + name;
+             url += "&amp;form_token=$!{services.csrf.getToken()}";
              new Ajax.Request(url, 
              {
                method: 'get',
Index: web/standard/src/main/webapp/templates/getgroupmembers.vm
===================================================================
--- web/standard/src/main/webapp/templates/getgroupmembers.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/getgroupmembers.vm	(working copy)
@@ -44,7 +44,7 @@
     "doc_hasadmin" : "${xwiki.hasAccessLevel('admin', $context.user, $doc)}",
     "member"   : "$xwiki.getUserName($m, false)#if($hasAdmin || $isAdvancedUser) (#if($wikiname != 'local')$wikiname:#end$m)#end",
     "member_url" : "$xwiki.getURL($m)",
-    "doc_delete_url" : "$doc.getURL('view', "xpage=deletegroupmember&fullname=${m}&ajax=true")"
+    "doc_delete_url" : "$doc.getURL('view', "xpage=deletegroupmember&fullname=${m}&ajax=true&form_token=$!{services.csrf.getToken()}")"
    }
 #end
 ]}
Index: web/standard/src/main/webapp/templates/attachwysiwyg.vm
===================================================================
--- web/standard/src/main/webapp/templates/attachwysiwyg.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/attachwysiwyg.vm	(working copy)
@@ -2,6 +2,8 @@
 <div id="xwikiattachmentscontent" class="xwikidatacontent">
   <form id="attach" action="$doc.getURL("upload")" enctype="multipart/form-data" method="post"
         onsubmit="return updateAttachName(this)" >
+    ## CSRF prevention
+    <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
     <input type="hidden" name="xredirect" value="" />
     <p>
       $msg.get("choosetargetfilename"):
Index: web/standard/src/main/webapp/templates/rename.vm
===================================================================
--- web/standard/src/main/webapp/templates/rename.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/rename.vm	(working copy)
@@ -25,6 +25,9 @@
     #if($step == 1)
       ## Display the rename form
       #template("renameStep1.vm")
+    #elseif(!${services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
+      ## CSRF protection
+      $response.sendRedirect("$!{services.csrf.getResubmissionURL()}")
     #else
       ## Do the rename. It's the form in renameStep1.vm page that calls this page with step=2.
       #if((!$request.newPageName) || ($request.newPageName.trim().equals("")))
Index: web/standard/src/main/webapp/templates/importinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/importinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/importinline.vm	(working copy)
@@ -40,6 +40,8 @@
       ## Let the user upload XAR files.
       <form action="$doc.getURL('upload')" enctype="multipart/form-data" method="post" id="AddAttachment">
         <div>
+          ## CSRF prevention
+          <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
           <input type="hidden" name="xredirect" value="${xwiki.getFormEncoded($redirect)}" />
           <fieldset id="attachform">
             ## Temporarily disabled, until we fix attachment name handling
@@ -72,7 +74,7 @@
                 <span class="version">$attach.version</span>
                 <span class="xwikibuttonlinks">
                   #set($redirect = $doc.getURL('import', 'section=Import'))
-                  #set($attachurl = $doc.getAttachmentURL(${attach.filename}, 'delattachment', "xredirect=$!{redirect}"))
+                  #set($attachurl = $doc.getAttachmentURL(${attach.filename}, 'delattachment', "xredirect=$!{redirect}&amp;form_token=$!{services.csrf.getToken()}"))
                   <a class="deletelink" href="${attachurl}"
                      onclick="return confirm('$msg.get('core.viewers.attachments.delete.confirm')');"
                      title="$msg.get('core.viewers.attachments.delete.title')"></a>
@@ -141,6 +143,8 @@
             </div>
             <form action="$!{importaction}" method="post" class="static-importer">
               <div>
+                ## CSRF prevention
+                <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
                 <input type="hidden" name="action" value="import" />
                 <input type="hidden" name="name" value="$request.file" />
                 <div id="package">
Index: web/standard/src/main/webapp/templates/addnewgroup.vm
===================================================================
--- web/standard/src/main/webapp/templates/addnewgroup.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/addnewgroup.vm	(working copy)
@@ -3,7 +3,7 @@
 <form method="post" onsubmit="return false;">
 <div>
 <span style="padding-left:13px;"><input type="text" name="newgroup" value="" id="newgroupi" /></span>
-<span class="buttonwrapper"><input class="button" type="submit" value="$msg.get("rightsmanager.creategroup")" onclick="new Ajax.Request('$xwiki.getURL("XWiki.__groupname__", "save", "template=XWiki.XWikiGroupTemplate")'.replace('__groupname__', this.form.newgroup.value), { onFailure: function(transport) { alert('$msg.get("rightsmanager.groupexist").replaceAll('"', '&quot;')'.replace('__name__', $('newgroupi').value)); }, onSuccess: function(transport) { window.lb.lbSaveForm(); } });" /></span>
+<span class="buttonwrapper"><input class="button" type="submit" value="$msg.get("rightsmanager.creategroup")" onclick="new Ajax.Request('$xwiki.getURL("XWiki.__groupname__", "save", "form_token=$!{services.csrf.getToken()}&template=XWiki.XWikiGroupTemplate")'.replace('__groupname__', this.form.newgroup.value), { onFailure: function(transport) { alert('$msg.get("rightsmanager.groupexist").replaceAll('"', '&quot;')'.replace('__name__', $('newgroupi').value)); }, onSuccess: function(transport) { window.lb.lbSaveForm(); } });" /></span>
 <span class="buttonwrapper"><input class="button" type="submit" name="formactioncancel" value="$msg.get("cancel")" onclick=" window.lb.lbHide(); window.lb.lbClearData(); return false;"/></span>
 </div>
 </form>
Index: web/standard/src/main/webapp/templates/registerinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/registerinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/registerinline.vm	(working copy)
@@ -23,6 +23,8 @@
   #if(!$reg || $reg < 0)
     <form id="register" action="" method="post">
       <div>
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
         <input type="hidden" name="template" value="XWiki.XWikiUserTemplate" />
         <input type="hidden" name="register" value="1"/>
         #set($class = $xwiki.getClass('XWiki.XWikiUsers'))
Index: web/standard/src/main/webapp/templates/deletegroupmember.vm
===================================================================
--- web/standard/src/main/webapp/templates/deletegroupmember.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/deletegroupmember.vm	(working copy)
@@ -7,6 +7,7 @@
   #set($discard = $doc.newObject("XWiki.XWikiGroups"))
   added empty group
 #end
+#if ($!{services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
 #set($discard = $doc.save())
 #if($request.ajax)
   ok
@@ -14,4 +15,7 @@
   $response.sendRedirect($request.xredirect)
 #else
   $response.sendRedirect($doc.getURL('view'))
+#end
+#else
+  notallowed
 #end
\ No newline at end of file
Index: web/standard/src/main/webapp/templates/recyclebinlist.vm
===================================================================
--- web/standard/src/main/webapp/templates/recyclebinlist.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/recyclebinlist.vm	(working copy)
@@ -16,10 +16,10 @@
   <tr>
    <td>$xwiki.getLocalUserName($dd.getDeleter())</td>
    <td><a href="$doc.getURL("view", "viewer=recyclebin&amp;id=$dd.getId()")">$xwiki.formatDate($dd.getDate())</a></td>
-   <td>#if($dd.canUndelete()) <a href="$xwiki.getURL($tdoc.getFullName(), "undelete", "id=$dd.getId()")">$msg.get("core.recyclebin.restore")</a> #end</td>
+   <td>#if($dd.canUndelete()) <a href="$xwiki.getURL($tdoc.getFullName(), "undelete", "form_token=$!{services.csrf.getToken()}&amp;id=$dd.getId()")">$msg.get("core.recyclebin.restore")</a> #end</td>
    <td>
     #if($dd.canDelete())
-     <a href="$xwiki.getURL($tdoc.getFullName(), "delete", "id=$dd.getId()")"
+     <a href="$xwiki.getURL($tdoc.getFullName(), "delete", "form_token=$!{services.csrf.getToken()}&amp;id=$dd.getId()")"
       onclick="if (confirm('$msg.get("core.recyclebin.completelyDeleteConfirm")')) {this.href += '&amp;confirm=1'; return true;} return false;">
       $msg.get("core.recyclebin.delete")
      </a>
Index: web/standard/src/main/webapp/templates/saverights.vm
===================================================================
--- web/standard/src/main/webapp/templates/saverights.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/saverights.vm	(working copy)
@@ -7,6 +7,7 @@
 #set($founda = false)
 #set($foundd = false)
 
+#if ($!{services.csrf.isTokenValid("$!{request.getParameter('form_token')}")})
 #foreach($obj in $doc.getObjects($clsname)) ## XWiki.XWikiGlobalRights or XWiki.XWikiRights
   #set($pers = "$!obj.getProperty($uorg).getValue()")
   #if($pers.matches("${fullname},?")) ## found the rule containing that person
@@ -100,4 +101,5 @@
   $obj.set('allow', '0')
 #end
 $doc.save($msg.get("platform.core.rightsManagement.saveComment", [${action}, ${right}, ${fullname}]), true)
-SUCCESS
\ No newline at end of file
+SUCCESS
+#end
\ No newline at end of file
Index: web/standard/src/main/webapp/templates/registeruser.vm
===================================================================
--- web/standard/src/main/webapp/templates/registeruser.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/registeruser.vm	(working copy)
@@ -22,6 +22,8 @@
 #if(!$reg||$reg<0 )
     <form id="register" name="register" action="" method="post">
       <div>
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
         <input type="hidden" name="template" value="XWiki.XWikiUserTemplate" />
         <input type="hidden" name="register" value="1"/>
 #set( $class = $xwiki.getClass("XWiki.XWikiUsers"))
Index: web/standard/src/main/webapp/templates/edituser.vm
===================================================================
--- web/standard/src/main/webapp/templates/edituser.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/edituser.vm	(working copy)
@@ -1,6 +1,8 @@
 #template("xwikivars.vm")
 <div>
 <form name="edituser" method="post">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 #set($force = $!request.get("force"))
 #if (($doc.getLocked()==true)&&(!$force))
 <div>
Index: web/standard/src/main/webapp/templates/rightsUI.vm
===================================================================
--- web/standard/src/main/webapp/templates/rightsUI.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/rightsUI.vm	(working copy)
@@ -42,7 +42,7 @@
 
     ## url to take the users and groups to display in the ajax-based table
     #set($url = $doc.getURL('get', 'xpage=getusersandgroups'))
-    #set($saveUrl = $doc.getURL("edit", "xpage=saverights&clsname=${clsname}&fullname=XWiki.XWikiGuest&uorg=users"))
+    #set($saveUrl = $doc.getURL("edit", "form_token=$!{services.csrf.getToken()}&xpage=saverights&clsname=${clsname}&fullname=XWiki.XWikiGuest&uorg=users"))
 
     ## get the rights for XWikiGuest
     #set($r = $util.arrayList)
@@ -186,7 +186,9 @@
         saveUrl.replace(/&amp;/g, "&");
         window.currentUser = "$context.user";
         window.unregUser = "XWiki.XWikiGuest";
-        var ta = new XWiki.widgets.LiveTable("$url", "usersandgroupstable", displayUsersAndGroups, {"filtersNode": $('usersandgroupstable')});
+        // the callback function is called from LiveTable with 3 arguments
+        var callback = function(row, i, table, idx) { return displayUsersAndGroups(row, i, table, idx, "$!{services.csrf.getToken()}") };
+        var ta = new XWiki.widgets.LiveTable("$url", "usersandgroupstable", callback, {"filtersNode": $('usersandgroupstable')});
         #foreach($i in [0..$maxlevel])
         var chbx${i} = new MSCheckbox($("td${levelsRights.get($i)}"), "${levelsRights.get($i)}", saveUrl, "${r.get($i)}");
         #end
@@ -197,11 +199,11 @@
 		   $('unregistered').removeClassName('hidden');
 		});
         #if("$!editor" == "globaladmin")
-    Event.observe($('authenticate_view'), 'click', setBooleanPropertyFromLiveCheckbox($('authenticate_view'), '$xwiki.getURL("XWiki.XWikiPreferences", "save")', 'XWiki.XWikiPreferences', 0));
-    Event.observe($('authenticate_edit'), 'click', setBooleanPropertyFromLiveCheckbox($('authenticate_edit'), '$xwiki.getURL("XWiki.XWikiPreferences", "save")', 'XWiki.XWikiPreferences', 0));
+    Event.observe($('authenticate_view'), 'click', setBooleanPropertyFromLiveCheckbox($('authenticate_view'), '$xwiki.getURL("XWiki.XWikiPreferences", "save", "form_token=$!{services.csrf.getToken()}")', 'XWiki.XWikiPreferences', 0));
+    Event.observe($('authenticate_edit'), 'click', setBooleanPropertyFromLiveCheckbox($('authenticate_edit'), '$xwiki.getURL("XWiki.XWikiPreferences", "save", "form_token=$!{services.csrf.getToken()}")', 'XWiki.XWikiPreferences', 0));
         #end
         #if($guest_comment_captcha_prop)
-    Event.observe($('guest_comment_requires_captcha'), 'click', setBooleanPropertyFromLiveCheckbox($('guest_comment_requires_captcha'), '$doc.getURL("save")', 'XWiki.XWikiPreferences', 0));
+    Event.observe($('guest_comment_requires_captcha'), 'click', setBooleanPropertyFromLiveCheckbox($('guest_comment_requires_captcha'), '$doc.getURL("save", "form_token=$!{services.csrf.getToken()}")', 'XWiki.XWikiPreferences', 0));
         #end
       }
       if ((typeof(XWiki) != 'undefined') && (typeof(XWiki.widgets) != 'undefined') && (typeof(XWiki.widgets.LiveTable) != 'undefined')) {
@@ -222,6 +224,8 @@
     #set( $class = $xwiki.getDocument($classname).xWikiClass)
     #set( $redirect = "$xwiki.getRequestURL()")
     <form id="update" method="post" action="$doc.getURL("save")" onsubmit="cancelCancelEdit()">
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
         <div style="overflow:auto">
             <div>
                 #set ($first = 1)
@@ -238,11 +242,11 @@
                         <td>$!doc.displayEdit($class.groups,"${class.name}_${obj.number}_",$obj)</td>
                         <td>$!doc.displayEdit($class.levels,"${class.name}_${obj.number}_",$obj)</td>
                         <td>$!doc.displayEdit($class.allow,"${class.name}_${obj.number}_",$obj)</td>
-                        <td class="xwikibuttonlink"><a href="$doc.getURL("objectremove", "classname=${class.name}&amp;classid=${obj.number}&amp;xredirect=${util.encodeURI($redirect)}")" onclick="return confirm('$msg.get("confirmobjectremove")')">$msg.get("Remove")</a></td>
+                        <td class="xwikibuttonlink"><a href="$doc.getURL("objectremove", "form_token=$!{services.csrf.getToken()}&amp;classname=${class.name}&amp;classid=${obj.number}&amp;xredirect=${util.encodeURI($redirect)}")" onclick="return confirm('$msg.get("confirmobjectremove")')">$msg.get("Remove")</a></td>
                     </tr>
                     #end
                   </table>
-				  <a href="$doc.getURL("objectadd", "classname=${class.name}&amp;xredirect=${util.encodeURI($redirect)}")">$msg.get("addrightentry")</a>
+				  <a href="$doc.getURL("objectadd", "form_token=$!{services.csrf.getToken()}&amp;classname=${class.name}&amp;xredirect=${util.encodeURI($redirect)}")">$msg.get("addrightentry")</a>
             </div> ## noname
         </div> ## overflow
 		<div class="hidden">
Index: web/standard/src/main/webapp/templates/passwd.vm
===================================================================
--- web/standard/src/main/webapp/templates/passwd.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/passwd.vm	(working copy)
@@ -10,6 +10,8 @@
 #set($i = $context.user.indexOf(":")+1)
 #if($context.user.substring($i) == $doc.fullName || $hasAdmin)
   <form action="$doc.getURL("save")" method="post" onsubmit="if($('xwikipassword').value != $('xwikipassword2').value) {alert('$msg.get("platform.core.profile.passwd.passwordMissmatch")'); return false; } else if($('xwikipassword').value == '') {alert('$msg.get("platform.core.profile.passwd.passwordCannotBeEmpty")'); return false;}">
+    ## CSRF prevention
+    <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <h1>$msg.get("platform.core.profile.passwd.title", [$xwiki.getXMLEncoded($xwiki.getUserName($doc.fullName, false))])</h1>
 <dl>
     <dt><label id="xwikipasswordlabel" for="xwikipassword">$msg.get("platform.core.profile.passwd.newPassword"):</label></dt>
Index: web/standard/src/main/webapp/templates/editwysiwygnew.vm
===================================================================
--- web/standard/src/main/webapp/templates/editwysiwygnew.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editwysiwygnew.vm	(working copy)
@@ -10,6 +10,8 @@
 ## <div class="topbuttons">#template("editactions.vm")</div>
 <div>
 <div class="hidden">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <input type="hidden" name="template" value="$!request.template" />
 #if($request.section && $request.section!="")
  #set($xcontinue=$doc.getURL("edit", "editor=wysiwyg&amp;section=$request.section"))
Index: web/standard/src/main/webapp/templates/attachmentsinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/attachmentsinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/attachmentsinline.vm	(working copy)
@@ -24,7 +24,7 @@
 #if($hasEdit || $hasAdmin)
           <span class="xwikibuttonlinks">
             ## Delete attachment link
-            <a class="deletelink" href="$doc.getAttachmentURL(${attach.filename}, 'delattachment', "xredirect=${redirect}")"  title="$msg.get('core.viewers.attachments.deleteTitle', [$xwiki.getXMLEncoded($attach.filename)])">$msg.get('core.viewers.attachments.delete')</a>
+            <a class="deletelink" href="$doc.getAttachmentURL(${attach.filename}, 'delattachment', "form_token=$!{services.csrf.getToken()}&amp;xredirect=${redirect}")"  title="$msg.get('core.viewers.attachments.deleteTitle', [$xwiki.getXMLEncoded($attach.filename)])">$msg.get('core.viewers.attachments.delete')</a>
             ## Dav Edit attachment link. We display the link hidden by default, and let the WebDAV JSX extension display it if
             ## the browser supports Dav Edit 
 	    #set($documentUrl=$doc.getExternalURL("download"))
@@ -60,6 +60,8 @@
 #end
 #if($hasEdit || $hasAdmin)
       <form action="$doc.getURL("upload")" enctype="multipart/form-data" method="post" id="AddAttachment">
+        ## CSRF prevention
+        <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <div>
 <input type="hidden" name="xredirect" value="${xwiki.getFormEncoded($redirect)}" />
 <fieldset id="attachform">
Index: web/standard/src/main/webapp/templates/editclass.vm
===================================================================
--- web/standard/src/main/webapp/templates/editclass.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editclass.vm	(working copy)
@@ -9,6 +9,8 @@
 #set($class = $doc.getxWikiClass())
 #set($redirect = $xwiki.getRequestURL())
 <form id="$formname" method="post" action="$doc.getURL("preview")" onsubmit="cancelCancelEdit()">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <div id="xwikieditcontent" class="clear">
 #set($hiddenProperties = ['tooltip', 'customDisplay', 'unmodifiable', 'contenttype'])
 ##
Index: web/standard/src/main/webapp/templates/editinline.vm
===================================================================
--- web/standard/src/main/webapp/templates/editinline.vm	(revision 29535)
+++ web/standard/src/main/webapp/templates/editinline.vm	(working copy)
@@ -27,6 +27,8 @@
 #set($xredirect=$!request.getParameter("xredirect"))
 #set($xnotification=$!request.getParameter("xnotification"))
 <form id="inline" method="post" action="$doc.getURL("preview")">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <div>
 <div class="hidden"><input type="hidden" name="xcontinue" value="$doc.getURL("inline")"/></div>
 ## This input is used by the full screen widget: it stores the selector of the maximized element, so that the full screen can be restored when coming back from preview.
Index: skins/colibri/src/main/resources/colibri/editinline.vm
===================================================================
--- skins/colibri/src/main/resources/colibri/editinline.vm	(revision 29535)
+++ skins/colibri/src/main/resources/colibri/editinline.vm	(working copy)
@@ -38,6 +38,8 @@
 #set($xredirect=$!request.getParameter("xredirect"))
 #set($xnotification=$!request.getParameter("xnotification"))
 <form id="inline" method="post" action="$doc.getURL("preview")">
+## CSRF prevention
+<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
 <div>
 <div class="hidden"><input type="hidden" name="xcontinue" value="$doc.getURL("inline")"/></div>
 ## This input is used by the full screen widget: it stores the selector of the maximized element, so that the full screen can be restored when coming back from preview.
Index: skins/colibri/src/main/resources/colibri/edit.vm
===================================================================
--- skins/colibri/src/main/resources/colibri/edit.vm	(revision 29535)
+++ skins/colibri/src/main/resources/colibri/edit.vm	(working copy)
@@ -33,6 +33,8 @@
 #template("header.vm")
 #if($editor == 'wiki' || $editor == 'wysiwyg')
   <form id="edit" method="post" action="$doc.getURL('preview')" >
+  ## CSRF prevention
+  <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
   ## Pass the section number when the form is submitted in order to affect only the specified section.
   ## We need to test if the section number is present to prevent a NumberFormatException for the empty string.
   #if("$!{request.section}" != '')
